package networking;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.concurrent.Callable;

import networking.messages.ProximityMessage;
import networking.messages.SimpleMessage;
import networking.messages.createMessages.HunterCreateMessage;

import logic.common.team.Team;
import logic.nodes.collision.DummyType;
import logic.ships.Ship;
import logic.ships.hunter.Hunter;

import com.captiveimagination.jgn.JGN;
import com.captiveimagination.jgn.clientserver.JGNServer;
import com.captiveimagination.jgn.synchronization.message.SynchronizeCreateMessage;
import com.jme.math.Vector3f;
import com.jme.util.GameTaskQueueManager;

import factories.HunterFactory;
import gameStates.absGamesStates.network.AbsServerState;

public class ServerSyncManager extends SyncManager {
	
	private JGNServer server;
	private AbsServerState serverState;
	private ServerInfoThread serverInfoThread;
	private PingThread pingThread;
	
	public ServerSyncManager(String ip, AbsServerState serverState) throws Exception {
		super(serverState);
		this.serverState = serverState;
		
		InetAddress serverAddr = InetAddress.getByName(ip);
		InetSocketAddress serverReliable = new InetSocketAddress(serverAddr, TCPPORT);
		InetSocketAddress serverFast = new InetSocketAddress(serverAddr, UDPPORT);
		
		server = new JGNServer(serverReliable, serverFast);
		server.addClientConnectionListener(serverState);
		server.addMessageListener(serverState);
		JGN.createThread(server).start();
		
		initSyncManager();
		
		serverInfoThread = new ServerInfoThread(ingameState);
		serverInfoThread.start();
		
		pingThread = new PingThread(serverState);
		pingThread.start();
	}
	
	public void sendMessageToAll(SimpleMessage message) {
		if(message instanceof ProximityMessage) {
			ProximityMessage pm = (ProximityMessage)message;
			if(!pm.applies()) server.sendToAll(message);
			else {
				Vector3f location = pm.getLocation();
				HashMap<Short, Integer> humanPlayersShort = serverState.getHumanPlayersShort();
				for(Short playerId : humanPlayersShort.keySet()) {
					short id = playerId.shortValue();
					boolean tooFar = serverState.isTooFarAway(location, id);
					if(!tooFar) sendMessageToPlayer(message, id);
				}
			}
		} else server.sendToAll(message);
	}
	
	public void sendToAllExcept(SimpleMessage message, short id) {
		server.sendToAllExcept(message, id);
	}
	
	public void sendMessageToPlayer(SimpleMessage message, short playerId) {
		server.sendToPlayer(message, playerId);
	}
	
	public void endServer() {
		if(server == null) return;
		
		try {
			serverInfoThread.killThread();
			if(pingThread != null) pingThread.killThread();
			graphicSyncManager.shutdown();
			server.close();
			
			long startTime = System.currentTimeMillis();
			while((server.isAlive() || graphicSyncManager.isAlive() || serverInfoThread.isAlive()) 
			&& System.currentTimeMillis() - startTime <= 5000) {}
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			cleanup();
			pingThread = null;
			serverInfoThread = null;
			graphicController = null;
			graphicSyncManager = null;
			server = null;
		}
	}
	
	@Override
	protected SynchronizationManager getNewSyncManager() {
		return new SynchronizationManager(server, graphicController);
	}
	
	@Override
	public Object create(SynchronizeCreateMessage message) {
		if(message == null) return null;
		if(message instanceof HunterCreateMessage) {
			if(ingameState.getGame() == null) return null;
			
			final HunterCreateMessage hcm = (HunterCreateMessage)message;
			if(hcm.getFraction() == null) return null;
			final Team team = ingameState.getGame().getTeam(hcm.getFraction());
			
			if(hcm.getType() == null) return null;
			
			try {
				return GameTaskQueueManager.getManager().update(new Callable<Ship>() {
					@Override
					public Ship call() throws Exception {
						String hunterName = hcm.getHunterName();
						Hunter hunter = HunterFactory.createNewHunter(hunterName, team, DummyType.Client);
						
						serverState.registerTransmittedHunter(hunter, hcm.getPlayerId());
						putPlayerToHunter(hunter, hcm.getPlayerID());
						
						ingameState.addNode(hunter);
						
						return hunter;
					}
				}).get();
			} catch(Exception e) { e.printStackTrace(); }
		}
		return null;
	}
}